package com.tinyshardingjdbc.tinyshardingjdbc.shardingasync.core;


import com.tinyshardingjdbc.tinyshardingjdbc.shardingasync.TinyShardingAsyncImpl;
import com.tinyshardingjdbc.tinyshardingjdbc.shardingasync.TinyShardingAsyncServiceImpl;
import com.tinyshardingjdbc.tinyshardingjdbc.shardingasync.annotaion.TinyShardingJdbcVo;
import com.tinyshardingjdbc.tinyshardingjdbc.shardingasync.tool.ExecutorPluginUtils;
import com.tinyshardingjdbc.tinyshardingjdbc.shardingasync.tool.TinyApplicationContext;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Map;


public class Processor implements SqlProcessor {
    @Autowired
    TinyShardingAsyncImpl tinyShardingAsync;
    @Autowired
    TinyShardingAsyncServiceImpl tinyShardingAsyncService;

    @Override
    public Object sqlProcessor(TinyShardingJdbcVo tinyShardingJdbcVo, Invocation invocation) throws Exception {
        Object[] args = invocation.getArgs();
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Object parameter = args[1];
        Object target = invocation.getTarget();
        BoundSql boundSql = null;
        Executor executor = (Executor) target;
        RowBounds rowBounds = null;
        ResultHandler resultHandler = null;
        if (mappedStatement.getSqlCommandType() == SqlCommandType.SELECT) {
            rowBounds = (RowBounds) args[2];
            resultHandler = (ResultHandler) args[3];
        }
        if (args.length < 5) {
            boundSql = mappedStatement.getBoundSql(parameter);
        } else {
            boundSql = (BoundSql) args[5];
        }
        List<ParameterMapping> parameterList = boundSql.getParameterMappings();
        Map<String, Object> map = ExecutorPluginUtils.parsePar(parameter, parameterList);
        if (tinyShardingJdbcVo.isPage()) {
            String tableStart = ExecutorPluginUtils.findParameter(parameter, boundSql, tinyShardingJdbcVo.getTableStart());
            String tableEnd = ExecutorPluginUtils.findParameter(parameter, boundSql, tinyShardingJdbcVo.getTableEnd());
            String filed = ExecutorPluginUtils.findParameter(parameter, boundSql, tinyShardingJdbcVo.getFiled());
            return tinyShardingAsync.queryByPage(tableStart, tableEnd,
                    tinyShardingAsyncService,
                    tinyShardingJdbcVo.getTableName(),
                    parameter,
                    boundSql, tinyShardingJdbcVo.getPage(), invocation, map, tinyShardingJdbcVo, filed);
        } else {
            try {
                String tabelPrx = "";
                if (!map.containsKey(tinyShardingJdbcVo.getFiled())) {
                    throw new Exception("分页需要传" + tinyShardingJdbcVo.getFiled() + "字段");
                }
                /**
                 * 需要分实现
                 * */
                String np = String.valueOf(map.get(tinyShardingJdbcVo.getFiled()));
                TypeProcessor typeProcessor = (TypeProcessor) TinyApplicationContext.getBean(tinyShardingJdbcVo.getTinyShardingJdbc().clz());
                tabelPrx = typeProcessor.getTableNameSuffix(np, tinyShardingJdbcVo, invocation);
                String sql = boundSql.getSql().replace(tinyShardingJdbcVo.getTableName(), tinyShardingJdbcVo.getTableName() + "_" + tabelPrx);
                ExecutorPluginUtils.resetSql2Invocation(invocation, sql, boundSql, mappedStatement);
                mappedStatement = (MappedStatement) invocation.getArgs()[0];
                if (mappedStatement.getSqlCommandType() == SqlCommandType.SELECT) {
                    CacheKey cacheKey = executor.createCacheKey(mappedStatement, parameter, rowBounds, boundSql);
                    return executor.query(mappedStatement, parameter, rowBounds, resultHandler, cacheKey, boundSql);
                } else {
                    return executor.update(mappedStatement, parameter);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return null;
    }


}
